/*
 * Copyright (C) 2010, Funcom AS, All Rights Reserved
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  @authors
 *  Svein Gunnar Larsen <sveingunnarlarsen@gmail.com>
 *  Erik Thune Lund <culex.eriklund@gmail.com>
 *  Michel Krohn-Dale <kronicz@gmail.com>
 */

package com.funcom.peeler.tool;

import com.funcom.peeler.UpdateCont;
import com.funcom.peeler.WorkBench;
import com.funcom.peeler.action.Change;
import com.funcom.peeler.action.Delete;
import com.funcom.peeler.action.MultiChange;
import com.funcom.peeler.action.Place;
import com.funcom.peeler.component.WorkSpace;
import com.funcom.peeler.peelable.PeelCanvasWindow;
import com.funcom.peeler.peelable.PeelComponent;
import com.funcom.peeler.symbol.Symbol;

import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;

import static com.funcom.peeler.component.PeelComponentFactory.copyComponent;

public class DefaultTool extends Tool {

    Point prev_coords;
    Point delta = new Point();

    PeelComponent component;

    public DefaultTool( WorkBench workbench ) {
        super( workbench );
    }

    public void keyPressed( KeyEvent e ) {

        switch ( e.getKeyCode() ) {

            // Key combos for copy, cut and paste
            case KeyEvent.VK_C: {
                if ( e.isControlDown() )
                copy();
                break;
            }

            case KeyEvent.VK_X: {
                if ( e.isControlDown() ) {
                    cut();
                }
                break;
            }

            case KeyEvent.VK_V: {
                if ( e.isControlDown() ) {
                    paste();
                }
                break;
            }

            // Export the canvas contents to file if  Ctrl + E is pressed
            case KeyEvent.VK_E: {
                if ( e.isControlDown() ) workbench.exportCanvas();
                break;
            }

            // Activate closing of the WorkBench if Ctrl + Q is pressed
            case KeyEvent.VK_Q: {
                if ( e.isControlDown() ) workbench.close();
                break;
            }

            // Enable and disable the display of the grid by pressing the G key
            case KeyEvent.VK_G: {
                if ( e.getModifiers() == 0 ) {
                    bui.getPeelRoot().getToolBar().setGridEnabled( !bui.getPeelRoot().getToolBar().gridEnabled());
                }

                if ( e.isControlDown() ) {

                    if ( e.isShiftDown() ) {
                        ungroupSelections();
                    }
                    else {
                        groupSelections();
                    }
                }if( e.isAltDown() ){
                    bui.getPeelRoot().getToolBar().setSnapEnabled( !bui.getPeelRoot().getToolBar().snapEnabled());
                }
                break;
            }

            // Shortcut for selecting the Mark/Select tool
            case KeyEvent.VK_M: {
                UpdateCont.get().getToolbox().setTool( Symbol.ToolType.SELECT );
                break;
            }

            // Shortcut for selecting the Resize tool
            case KeyEvent.VK_P: {
                UpdateCont.get().getToolbox().setTool( Symbol.ToolType.PAN );
                break;
            }

            // Shortcut for selecting the Resize tool
            case KeyEvent.VK_R: {
                UpdateCont.get().getToolbox().setTool( Symbol.ToolType.RESIZE );
                break;
            }

            // shortcut for selecting the MultiTool
            case KeyEvent.VK_ESCAPE:
            case KeyEvent.VK_S: {
                UpdateCont.get().getToolbox().setTool( Symbol.ToolType.MULTI );
                break;
            }

            // Delete selected components from the workspace if the Delete key is pressed
            case KeyEvent.VK_DELETE: {

                workbench.getBui().execute( new WorkBench.Task() {

                    public void run() {

                        // If there are no selections, there is nothing to delete
                        if ( workbench.getBui().getSelections().isEmpty() ) return;

                        // Add all the selection deletions into a MultiChange so they all appear/dissappear simultaneously
                        java.util.List<Change> deletes = new LinkedList< Change >();
                        for ( PeelComponent component : workbench.getBui().getSelections() ) {

                            if ( component.isLocked() ) continue;

                            if ( component instanceof PeelCanvasWindow) continue;

                            deletes.add( new Delete( component, bui, false ) );
                        }

                        if ( deletes.isEmpty() ) return;

                        MultiChange changes = new MultiChange( deletes );

                        changes.doChange();
                        workbench.pushChange( changes );
                        UpdateCont.get().newOrDeleteChange(true);

                    }
                });
                break;
            }

            // Reset the zoom level if Ctrl + 0 is pressed
            case KeyEvent.VK_0: {
                if ( e.isControlDown() ) resetZoom();
                break;
            }

            // Increase the zoom if Ctrl + plus-sign is pressed
            case KeyEvent.VK_PLUS: {
                if ( e.isControlDown() ) bui.setZoom( bui.getZoom() + 5 );
                break;
            }

            // Decrease the zoom if Ctrl + minus-sign is pressed
            case KeyEvent.VK_MINUS: {
                if ( e.isControlDown() ) bui.setZoom( bui.getZoom() - 5 );
                break;
            }
        }
    }

    public void mousePressed( MouseEvent e ) {

        if ( e.getButton() == e.BUTTON2 )
            prev_coords = e.getPoint();
    }

    public void mouseReleased( MouseEvent e ) {
        prev_coords = null;
    }

    public void mouseDragged( MouseEvent e ) {

        if ( prev_coords == null ) return;

        updateDelta( e );
        prev_coords = e.getPoint();

        WorkSpace workspace = bui.getWorkSpace().getComponent();
        workspace.setLocation( workspace.getX() + delta.x, workspace.getY() + delta.y );

        clampWorkSpaceBounds();
    }

    public void mouseWheelMoved( MouseWheelEvent e ) {

        // Only zoom the workspace if ctrl is held down
        if ( !e.isControlDown() ) return;

        float old_zoom = zoomFactor();

        bui.setZoom( bui.getZoom() + -e.getWheelRotation() * 5 );

        WorkSpace workspace = bui.getWorkSpace().getComponent();
        int loc_x = (int)( e.getX() * old_zoom - e.getX() * zoomFactor() );
        int loc_y = (int)( e.getY() * old_zoom - e.getY() * zoomFactor() );

        workspace.setLocation( workspace.getX() + loc_x, workspace.getY() + loc_y );
        clampWorkSpaceBounds();
    }

    private void clampWorkSpaceBounds() {

        WorkSpace workspace = bui.getWorkSpace().getComponent();
        Canvas sceneCanvas = bui.getCanvas();

        int x_loc_max = 0, y_loc_max = 0; // Set bounds for the position of the left and bottom edges of the workspace
        int x_loc_min = -(int)( workspace.getWidth() * zoomFactor() - sceneCanvas.getWidth() );
        int y_loc_min = -(int)( workspace.getHeight() * zoomFactor() - sceneCanvas.getHeight() );

        int loc_x = workspace.getX();
        int loc_y = workspace.getY();

        loc_x = loc_x > x_loc_max ? x_loc_max : loc_x;
        loc_x = loc_x < x_loc_min ? x_loc_min : loc_x;

        loc_y = loc_y > y_loc_max ? x_loc_max : loc_y;
        loc_y = loc_y < y_loc_min ? y_loc_min : loc_y;

        workspace.setLocation( loc_x, loc_y );
    }

    private void updateDelta( MouseEvent e ) {

        if ( prev_coords == null ) return;

        delta.x = e.getX() - prev_coords.x;
        delta.y = e.getY() - prev_coords.y;
    }

    private void resetZoom() {
        bui.setZoom( 100 );
    }

    private float zoomFactor() {
        return bui.getZoom() / 100.0f;
    }

    private void cut() {

        workbench.pushTool( this );
        copy();

        if (bui.getSelections().isEmpty()) return;

        PeelComponent component = (PeelComponent)bui.getSelections().toArray()[0];

        if ( component.isLocked() ) return;

        final Delete delete = new Delete( component, bui, true );
        workbench.pushChange( delete );

        bui.execute( new WorkBench.Task() {

            public void run() {
                delete.doChange();
            }
        });
    }

    private void groupSelections() {

        Set< PeelComponent > selections = bui.getSelections();

        if ( selections.isEmpty() || selections.size() < 2 ) return;

        Iterator< PeelComponent > iter = selections.iterator();
        PeelComponent grouper = iter.next();

        while ( iter.hasNext() ) {
            iter.next().addToGroup( grouper.getGroup() );
        }
    }

    private void ungroupSelections() {

        for ( PeelComponent component : bui.getSelections() ) {
            component.removeFromGroup();
        }
    }

    public void paste() {

        bui.execute( new WorkBench.Task() {

            public void run() {

                if (UpdateCont.get().hasCopy()) {

                    PeelComponent newComponent = UpdateCont.get().getComponentCopy();

                    if (newComponent.getComponent().getProperty("workbench") != workbench) {
                        workbench.getWorkBenchStyle().addAllNoneExistingStyles( newComponent, new ArrayList< String >() );                        
                    }

                    //Getting the coords of the canvas
                    Container temp = bui.getCanvas().getParent();
                    Point location = new Point(bui.getCanvas().getX(), bui.getCanvas().getY());

                    //Translate canvas coords to screen coords
                    while(temp != null){
                        location.translate(temp.getX(), temp.getY());
                        temp = temp.getParent();
                    }

                    //Translate mouse coords to canvas coords
                    location.x = MouseInfo.getPointerInfo().getLocation().x - location.x;
                    location.y = MouseInfo.getPointerInfo().getLocation().y - location.y;

                    //Invert Mouse Y axis
                    location.translate(0, bui.getCanvas().getHeight() - 2 * location.y);

                    //Translate mouse coords according to zoom
                    location.translate((int)(location.x / bui.getSceneRoot().getLocalScale().getX() - location.x),
                            (int)(location.y / bui.getSceneRoot().getLocalScale().getY() - location.y));

                    //Translate mouse coords to workspace coords
                    location.translate(-bui.getWorkSpace().getComponent().getX(), -bui.getWorkSpace().getComponent().getY());

                    newComponent.getComponent().setLocation(location.x, location.y);

                    bui.getWorkSpace().addChild( newComponent );

                    Place change = new Place( newComponent , bui.getWorkSpace(), bui );

                    workbench.pushChange( change );
                    bui.clearSelections();
                    bui.select( newComponent );
                    UpdateCont.get().componentHierarchyChanged();

                    newComponent = copyComponent( newComponent );

                    newComponent.getComponent().setProperty("workbench", workbench);

                    UpdateCont.get().sendComponentCopy( newComponent );
                }
            }
        });
    }

    public void copy() {

        bui.execute(new WorkBench.Task() {

            public void run() {

                if (bui.getSelections().isEmpty()) {
                    UpdateCont.get().getToolbox().setTool( Symbol.ToolType.MULTI );
                } else {
                    component = (PeelComponent)bui.getSelections().toArray()[0];

                    PeelComponent newComponent = copyComponent( component );

                    newComponent.getComponent().setProperty( "workbench", workbench );

                    UpdateCont.get().sendComponentCopy( newComponent );
                }
            }
        });
    }
}
